package container

import (
	"store/libs/helper"
	"sync"
	"time"
)

type waveItem struct {
	value    interface{}
	expireAt int64
}

/**
 * 存储条目
 */
type item struct {
	value    interface{}
	expireAt int64
}

var (
	defaultContainer *Container
)

type Container struct {
	data sync.Map
}

func init() {
	defaultContainer = NewContainer()
}

func NewContainer() *Container {
	return new(Container)
}

func (this *Container) Set(key string, value interface{}) bool {
	this.data.Store(key, item{
		value:    value,
		expireAt: 0,
	})
	return true
}

func (this *Container) SetTimeout(key string, value interface{}, timeout int64) bool {
	val := item{
		value: value,
	}

	if timeout < 1 {
		val.expireAt = 0
	} else {
		val.expireAt = time.Now().Unix() + timeout
	}

	this.data.Store(key, val)
	return true
}

func (this *Container) Get(key string) interface{} {
	value, _ := this.data.Load(key)
	val, ok := value.(item)
	if !ok {
		return nil
	}

	//非永久有效，且已经过期
	if val.expireAt > 0 && val.expireAt < time.Now().Unix() {
		return nil
	}

	return val.value
}

func (this *Container) Exists(key string) bool {
	value, ok := this.data.Load(key)
	if !ok {
		return false
	}

	val, ok := value.(item)
	if !ok {
		return false
	}

	if val.expireAt > 0 && val.expireAt < time.Now().Unix() {
		return false
	}

	return true
}

func (this *Container) Delete(keys ...string) bool {
	for _, key := range keys {
		this.data.Delete(key)
	}

	return true
}

func Set(key string, value interface{}) bool {
	return defaultContainer.Set(key, value)
}

func SetTimeout(key string, value interface{}, timeout int64) bool {
	return defaultContainer.SetTimeout(key, value, timeout)
}

func Get(key string) interface{} {
	return defaultContainer.Get(key)
}

func Exists(key string) bool {
	return defaultContainer.Exists(key)
}

func Delete(keys ...string) bool {
	return defaultContainer.Delete(keys...)
}

func WaveGet(key string, getData func() interface{}, timeout int64) interface{} {
	cKey := helper.Append("wave:", key)

	cValue := Get(cKey)
	item, ok := cValue.(waveItem)

	//container没有
	if !ok {
		value := getData()
		WaveSet(cKey, value, timeout)
		return value
	}

	//缓存未过期
	if item.expireAt > time.Now().Unix() {
		return item.value
	}

	//缓存过期,未获得锁
	if !helper.Acquire(cKey, 5) {
		return item.value
	}

	defer helper.Release(cKey)

	value := getData()
	WaveSet(cKey, value, timeout)
	return value
}

func WaveSet(cKey string, value interface{}, timeout int64) bool {
	//container不允许永不过期
	if timeout < 1 {
		timeout = 60000
	}

	Set(cKey, waveItem{
		value:    value,
		expireAt: time.Now().Unix() + timeout,
	})

	return true
}
